#pragma once
#include <map>
#include <list>
#include <functional>

#ifndef _STD
#define _STD ::std::
#endif

struct sockaddr_in;

class UDP
{
 
public:
	UDP();
    virtual ~UDP();

    virtual bool Initialize(const char* ip, uint16_t port);

    virtual void Terminate();

    virtual void Update(uint32_t timeout = 0);

	virtual void SendTo(uint8_t* buffer, uint32_t len, const char* ip, const uint16_t port);

	virtual void SendTo(uint8_t* buffer, uint32_t len, sockaddr_in* remote);

	static int64_t GetTimestamp();

protected:

    virtual void OnRecvMsg(uint8_t* buffer, uint32_t len, sockaddr_in* addr);

    void ErrorLog(const char* _Format, ...);


private:

    void HandleRecv();

public:

	template <class _Fx, class _Types>
	void RegisterRecvCallback(_Fx&& _Func, _Types&& _User) {
        onRecv = std::bind(_STD forward<_Fx>(_Func), _User, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
	}
	template <class _Fx>
	void RegisterRecvCallback(_Fx&& _Func) {
        onRecv = std::bind(_STD forward<_Fx>(_Func), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
	}
	void UnregisterRecvCallback() {
        onRecv = nullptr;
	}

	template <class _Fx, class _Types>
	void RegisterErrorLogCallback(_Fx&& _Func, _Types&& _User) {
		onErrorLog = std::bind(_STD forward<_Fx>(_Func), _User, std::placeholders::_1, std::placeholders::_2);
	}
	template <class _Fx>
	void RegisterErrorLogCallback(_Fx&& _Func) {
		onErrorLog = std::bind(_STD forward<_Fx>(_Func), std::placeholders::_1, std::placeholders::_2);
	}
	void UnregisterErrorLogCallback() {
		onErrorLog = nullptr;
	}

    void* User = nullptr;
private:

    uint64_t udpSocket = 0;
private:

    std::function<void(uint8_t* buffer, uint32_t len, sockaddr_in* remote, void* user)> onRecv;
	std::function<void(const char* log, void* user)> onErrorLog;
};

